"""
TODO: Port voting to "ranking backends" to reduce
      the amount of duplicate code
"""

from decimal import Decimal
from django.contrib.contenttypes.models import ContentType
from django.db import connection, models

def avg(l):
    """
    I cant believe this isnt in the 
    standard library. Honestly.
    """
    return sum(l)/len(l)

class VoteManager(models.Manager):
    def cast_vote(self, obj, user, score):
        """
        Register a ``Vote`` for 
        an object by a ``User``, if elligible
        """

        obj_ctype = ContentType.objects.get_for_model(obj)
        score = str(score)
        
        try:
            vote = self.get(user=user,
                            content_type=obj_ctype,
                            object_id=obj.pk)
            vote.score = score
            vote.save()
        except models.ObjectDoesNotExist:
            vote = self.create(user=user,
                               content_type=obj_ctype,
                               object_id=obj.pk,
                               score=score)


class VoteSummaryManager(models.Manager):
    """
    Manage vote tabulation

    Ranking uses ``VoteSummary.created_date``
    as the starting point when age is a variable.
    """
    def create_for_obj(self, obj):
        """
        Create a new ``VoteSummary`` for 
        a given object
        """
        obj_ctype = ContentType.objects.get_for_model(obj)

        vs, created = self.get_or_create(content_type=obj_ctype,
                                         object_id=obj.pk)

    def rank(self, ranker, model, limit=25, offset=0):
        """
        Rank a model's objects with a given ``Ranker``
        """

        if not ranker:
            from rangevoting.rankers import AvgRanker
            ranker = AvgRanker()
        else:
            ranker = ranker(vs_model=self.model)

        model_ctype = ContentType.objects.get_for_model(model)

        query = """
            select 
                `id`,
                `content_type_id`,
                `object_id`,
                %(rank_criteria)s as ranking
            from
                `%(db_table)s`
            where
                `vote_count` > 0
                and
                `content_type_id` = %(ctype_pk)s
            order by
                ranking desc
            limit %(offset)s, %(limit)s
        """

        query = query % {
            'rank_criteria': ranker.generate_criteria(),
            'ctype_pk': model_ctype.pk,
            'db_table': self.model._meta.db_table,
            'limit': limit,
            'offset': offset
            }

        cursor = connection.cursor()
        cursor.execute(query)
        results = cursor.fetchall()

        obj_pks = [result[2] for result in results]
        obj_dict = model.objects.in_bulk(obj_pks)
        objs = [obj_dict[pk] for pk in obj_pks if pk in obj_dict]

        return objs

    def get_obj_score(self, obj):
        """
        Get the current score for an object.

        Returns a ``dict`` with 
          * score
          * total (vote count)
        """
        
        obj_ctype = ContentType.objects.get_for_model(obj)

        query = """
            select 
                `vote_count`,
                `vote_sum` / `vote_count`
            from
                `%(db_table)s`
            where
                `content_type_id` = %(ct_pk)s
                and
                `object_id` = %(obj_pk)s
                and
                `vote_count` > 0;
        """

        query = query % {
            'db_table': self.model._meta.db_table,
            'ct_pk': obj_ctype.pk,
            'obj_pk': obj.pk
            }

        cursor = connection.cursor()
        cursor.execute(query)
        result = cursor.fetchone()

        if result:
            obj_score = {
                'total': int(result[0]), 
                'score': result[1] if result[1] else 0
                }
        else:
            obj_score = {'score': 0, 'total': 0}

        return obj_score
                    
    def get_ctype_avgs(self, ctypes=[]):
        """
        Get the avg scores for a set of
        ``ContentType``s and return a dict
        detailing the avg and total for each
        """
        raise NotImplementedError
